home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / prog_pas / tsfaqp31.zip / FAQPAS3.TXT < prev    next >
Text File  |  1996-04-28  |  57KB  |  1,493 lines

  1. From ts@uwasa.fi Sun Apr 28 00:00:00 1996
  2. Subject: FAQPAS3.TXT contents
  3.  
  4.                                Copyright (c) 1993-1996 by Timo Salmi
  5.                                                  All rights reserved
  6.  
  7. FAQPAS3.TXT The third set of frequently (and not so frequently)
  8. asked Turbo Pascal questions with Timo's answers. The items are in
  9. no particular order.
  10.  
  11. You are free to quote brief passages from this file provided you
  12. clearly indicate the source with a proper acknowledgment.
  13.  
  14. Comments and corrections are solicited. But if you wish to have
  15. individual Turbo Pascal consultation, please post your questions to
  16. a suitable Usenet newsgroup like news:comp.lang.pascal.borland. It
  17. is much more efficient than asking me by email. I'd like to help,
  18. but I am very pressed for time. I prefer to pick the questions I
  19. answer from the Usenet news. Thus I can answer publicly at one go if
  20. I happen to have an answer. Besides, newsgroups have a number of
  21. readers who might know a better or an alternative answer. Don't be
  22. discouraged, though, if you get a reply like this from me. I am
  23. always glad to hear from fellow Turbo Pascal users.
  24.  
  25. ....................................................................
  26. Prof. Timo Salmi   Co-moderator of news:comp.archives.msdos.announce
  27. Moderating at ftp:// & http://garbo.uwasa.fi archives  193.166.120.5
  28. Department of Accounting and Business Finance  ; University of Vaasa
  29. ts@uwasa.fi http://uwasa.fi/~ts BBS 961-3170972; FIN-65101,  Finland
  30.  
  31. --------------------------------------------------------------------
  32. 51) I am running out of memory when compiling my large program.
  33. 52) How do I avoid scrolling in the last column of the last row?
  34. 53) How can one hide (or unhide) a directory using a TP program?
  35. 54) How do I test whether a file is already open in a TP program?
  36. 55) How can I test and convert a numerical string into a real?
  37. 56) How can I reverse a TP .EXE or .TPU back into source code?
  38. 57) How can I calculate the difference between two points of time?
  39. 58) Is a program running stand-alone or from within the IDE?
  40. 59) Please explain Turbo Pascal memory addressing to me.
  41. 60) How do I obtain a bit or bits from a byte, a word or a longint?
  42. 61) What are Binary Coded Decimals? How to convert them?
  43. 62) How can I copy a file in a Turbo Pascal program?
  44. 63) How can I use C code in my Turbo Pascal program?
  45. 64) How do I get started with the Turbo Profiler?
  46. 65) How can I detect if the shift/ctrl/alt etc key is pressed?
  47. 66) How do I get a base 10 logarithm in TP?
  48. 67) If Delay procedure does not work properly, how do I fix it?
  49. 68) How much memory will my TP program require?
  50. 69) How to detect if a drive is a CD-ROM drive?
  51. 70) How do I convert an array of characters into a string?
  52. 71) How do I get started with graphics programming?
  53. 72) Where to I find the different sorting source codes?
  54. 73) A beginner's how to write and compile units.
  55. 74) What are and how do I use pointers?
  56. 75) How can I read another program's errorlevel value in TP?
  57. --------------------------------------------------------------------
  58.  
  59. From ts@uwasa.fi Sun Apr 28 00:00:51 1996
  60. Subject: Out of memory in compiling
  61.  
  62. 51. *****
  63.  Q: I am running out of memory when compiling my large program. What
  64.     can I do?
  65.  
  66.  A: If you are compiling your program from within the IDE (the
  67. Integrated Development Environment) then select the Option from the
  68. main menu, choose the Compiler item and set the Link buffer to
  69. Disk. (Also make the Compile option Destination to be Disk).
  70.    If this is not sufficient, next resort to using the TPC command
  71. line version of the Turbo Pascal compiler instead of the IDE.  Use
  72. the "Link buffer on disk" option.
  73.    Divide your program into units. It is advisable anyway for
  74. modularity when your program size grows.
  75.    If you have extended memory, instead of TURBO.EXE use TPX.EXE, if
  76. you have TP 7.0. If you are into protected mode programming then use
  77. Borland Pascal BP 7.0.
  78.  
  79.  A2: If you would prefer compiling your program from within the IDE
  80. but cannot do it for the above reason (or if you would prefer to
  81. compile your program from within your favorite editor instead of the
  82. TP IDE) you can use the following trick. If your editor has a macro
  83. language like most good editors do, the assign a hotkey macro that
  84. compiles the current file with the TPC. If you are using SemWare's
  85. QEdit editor you'll find such macros in ("Macros and configurations
  86. for QEdit text-editor") ftp://garbo.uwasa.fi/pc/ts/tsqed18.zip and
  87. in ftp://garbo.uwasa.fi/ts/tstse18.zip ("SAL macro sources to extend
  88. The SemWare Editor version 2.5").
  89.    Also your editor must be swapped to disk during the compilation
  90. if memory is critical. There is a very good program for doing that:
  91. ftp://garbo.uwasa.fi/pc/sysutil/shrom24b.zip ("Shell Room, Swap to
  92. disk when shelling to application"). For example I invoke the QEdit
  93. editor with using the following batch:
  94.  c:\tools\shroom -s r:\cmand -z 1024 c:\qedit\q %1 %2 %3 %4 %5 %6 %7
  95. You'll find more about the switches in the Shell Room documentation.
  96. The -s switch designates the swap destination (my r:\cmand directory
  97. is on my ramdisk). The -z switch sets the shell environment size.
  98.   An unfortunate part is that the TP 5.0 Turbo Pascal IDE is about
  99. the only program I know that is not amenable the to Shell Room
  100. utility, so you cannot utilize Shell Room to swap the TP IDE to
  101. disk. Blessfully, at least TP 7.0 no more has this problem.
  102. --------------------------------------------------------------------
  103.  
  104. From ts@uwasa.fi Sun Apr 28 00:00:52 1996
  105. Subject: Last position write woes
  106.  
  107. 52. *****
  108.  Q: How do I avoid scrolling in the last column of the last row?
  109.  
  110.  A: If you use write or writeln at the last column of the last row
  111. (usually 80,25) the screen will scroll. If you wish to avoid the
  112. scrolling you'll have to use an alternative write that does not move
  113. the cursor. Here is a procedure to write without moving the cursor
  114.   uses Dos;
  115.   procedure WriteChar (Character : char; fgColor, bgColor : byte);
  116.   var r : registers;
  117.   begin
  118.     FillChar (r, SizeOf(r), 0);
  119.     r.ah := $09;
  120.     r.al := ord(Character);
  121.     r.bl := (bgColor shl 4) or fgColor;
  122.     r.cx := 1;    { Number of repeats }
  123.     Intr ($10, r);
  124.   end;  (* writechar *)
  125. Thus, if you wish to write to the last column of the last row, you
  126. must first move the cursor to that position. That can be done in
  127. alternative ways. One might get there by having written previously
  128. on the screen (with writeln and write routines) until one is in that
  129. position. Another alternative is using GoToXY(80,20), but then you
  130. have to use the Crt unit. If you don't want to use it, then you can
  131. move the cursor by employing "GOATXY As the ordinary GoToXY but no
  132. Crt unit required" from ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  133.    There is an alternative interrupt service ($0A) which does the
  134. same as service $09, but uses the default colors instead. Just
  135. substitute $0A for $09, and leave the r.bl assignment out of the
  136. WriteChar routine.
  137.    Another option for writing anyhere on the screen without
  138. affecting the cursor is using direct screen writes:
  139.   uses Dos;
  140.   procedure WriteChar (c : char; x, y : byte; fg, bg : byte);
  141.   var vidstart : word;
  142.       regs     : registers;
  143.   begin
  144.     FillChar (regs, SizeOf(regs), 0);
  145.     regs.ah := $0F;
  146.     Intr ($10, regs);  { Color or MonoChrome video adapter }
  147.     if regs.al = 7 then vidstart := $B000 else vidstart := $B800;
  148.     mem[vidstart:((y-1)*80+x-1)*2] := ord(c);
  149.     mem[vidstart:((y-1)*80+x-1)*2+1] := (bg shl 4) or fg;
  150.   end;
  151. To write to the last position simply apply e.g.
  152.   WriteChar ('X', 80, 25, 14, 0);  { Yellow on black }
  153. The foreground (fg) and the background (bg) color codes are
  154.   Black        =   0
  155.   Blue         =   1
  156.   Green        =   2
  157.   Cyan         =   3
  158.   Red          =   4
  159.   Magenta      =   5
  160.   Brown        =   6
  161.   LightGray    =   7
  162.   DarkGray     =   8
  163.   LightBlue    =   9
  164.   LightGreen   =  10
  165.   LightCyan    =  11
  166.   LightRed     =  12
  167.   LightMagenta =  13
  168.   Yellow       =  14
  169.   White        =  15
  170.   Blink        = 128
  171. Yet another option is the following, but it needs the Crt unit. On
  172. the other hand, it uses the default color. This is quite a good and
  173. easy solution. I captured this fairly frequent idea from a posting
  174. by Robert Buergi (nbuero@hslrswi.hasler.ascom.ch).
  175.   uses Crt;
  176.   procedure WriteToCorner (c : char);
  177.   begin
  178.     Inc (WindMax);
  179.     GotoXY (80, 25);
  180.     write (c);
  181.     Dec (WindMax);
  182.   end;  (* writeToCorner *)
  183. --------------------------------------------------------------------
  184.  
  185. From ts@uwasa.fi Sun Apr 28 00:00:53 1996
  186. Subject: Hiding a directory
  187.  
  188. 53. *****
  189.  Q: How can one hide (or unhide) a directory using a TP program?
  190.  
  191.  A: Here is the code using interrupt programming. Incidentally,
  192. since MS-DOS 5.0 the attrib command can be used to hide and unhide
  193. directories.
  194. (* Hide a directory. Before using it would be prudent to check
  195.    that the directory exists, and that it is a directory.
  196.    With a contribution from Jan Nielsen jak@hdc.hha.dk
  197.    Based on information from Duncan (1986), p. 410 *)
  198. procedure HIDE (dirname : string);
  199. var regs : registers;
  200. begin
  201.   FillChar (regs, SizeOf(regs), 0);  { standard precaution }
  202.   dirname := dirname + #0;           { requires ASCIIZ strings }
  203.   regs.ah := $43;                    { function }
  204.   regs.al := $01;                    { subfunction }
  205.   regs.ds := Seg(dirname[1]);        { point to the name }
  206.   regs.dx := Ofs(dirname[1]);
  207.   regs.cx := 2; { set bit 1 on }     { to unhide set regs.cx := 0 }
  208.   Intr ($21, regs);                  { call the interrupt }
  209.   if regs.Flags and FCarry <> 0 then { were we successful }
  210.     writeln ('Failed to hide');
  211. end;  (* hide *)
  212.  
  213.  A2: An alternative method by Dr. Abimbola Olowofoyeku
  214. laa12@seq1.keele.ac.uk. No paths.
  215.   procedure HIDE (dirname : string);
  216.   var FileInfo : searchRec;
  217.       f        : file;
  218.   begin
  219.     FindFirst (dirname, Directory, FileInfo);
  220.     while DosError = 0 do
  221.       begin
  222.         assign (f, FileInfo.Name);
  223.         SetFAttr (f, Hidden);
  224.         FindNext (FileInfo);
  225.       end;
  226.   end;  (* hide *)
  227.   {}
  228.   procedure UNHIDE (dirname : string);
  229.   var FileInfo : searchRec;
  230.       f        : file;
  231.   begin
  232.     FindFirst (dirname, AnyFile, FileInfo);
  233.     while DosError = 0 do
  234.       begin
  235.         assign (f, FileInfo.Name);
  236.         SetFAttr (f, Archive);
  237.         FindNext (FileInfo);
  238.       end;
  239.   end;  (* unhide *)
  240. --------------------------------------------------------------------
  241.  
  242. From ts@uwasa.fi Sun Apr 28 00:00:54 1996
  243. Subject: Testing file opened status
  244.  
  245. 54. *****
  246.  Q: How do I test whether a file is already open in a TP program?
  247.  
  248.  A: This question is best answered by providing the code:
  249.   uses Dos;
  250.   {... for non-text files ...}
  251.   function ISFOPEN (var filePointer : file) : boolean;
  252.   begin
  253.     isfopen := FileRec(filePointer).mode <> FmClosed;
  254.   end;
  255.   {}
  256.   {... for text files ...}
  257.   function ISTOPEN (var filePointer : text) : boolean;
  258.   begin
  259.     istopen := TextRec(filePointer).mode <> FmClosed;
  260.   end;
  261.   {}
  262.   procedure TEST;          { Testing a non-text file }
  263.   const name = 'R:\TMP';
  264.   var f  : file;
  265.   begin
  266.     Assign (f, name);
  267.     writeln ('File ', name, ' is open is ', ISFOPEN(f));
  268.     {$I-} rewrite (f); {$I+}
  269.     if IOResult <> 0 then
  270.       begin
  271.         writeln ('Failed to open ', name);
  272.         exit;
  273.       end;
  274.     writeln ('File ', name, ' is open is ', ISFOPEN(f));
  275.     close(f);
  276.     writeln ('File ', name, ' is open is ', ISFOPEN(f));
  277.   end;
  278. --------------------------------------------------------------------
  279.  
  280. From ts@uwasa.fi Sun Apr 28 00:00:55 1996
  281. Subject: From string to real
  282.  
  283. 55. *****
  284.  Q: How can I test and convert a numerical string into a real?
  285.  
  286.  A1: An easy task in Turbo Pascal but in standard Pascal this
  287. frequent task is much trickier. Here are both the Turbo Pascal and
  288. Standard Pascal versions for general edification :-).
  289.   (* Convert and test a numerical string with Turbo Pascal *)
  290.   function DIGVALFN (mj : string; var ok : boolean) : real;
  291.   var k : integer;
  292.       x : real;
  293.   begin
  294.     Val (mj, x, k);
  295.     ok := k = 0;
  296.     if ok then digvalfn := x else digvalfn := 0;
  297.   end;  (* digvalfn *)
  298.   {}
  299.   (* Convert and test a numerical string with standard Pascal routines only *)
  300.   procedure DIGVAL (mj : string; var number : real; var ok : boolean);
  301.   label 1;
  302.   var il, lenl, pl, kl1, kl2 : integer;
  303.       nrol                   : boolean;
  304.       numberdl               : real;
  305.   begin
  306.     ok := true; lenl := Length (mj); nrol := false; pl := 0; number := 0.0;
  307.     if lenl = 0 then ok := false;
  308.     for il:=2 to lenl do if (mj[il]='-') or (mj[il]='+') then ok := false;
  309.     for il:=1 to lenl do
  310.       case mj[il] of
  311.         '0'..'9','+','-','.' : ; else ok := false;
  312.       end;
  313.     for il:=1 to lenl do
  314.       case mj[il] of '0'..'9' : begin nrol := true; goto 1; end; end;
  315.     1: if nrol = false then ok := false;
  316.     for il:=1 to lenl do if mj[il] = '.' then pl := pl + 1;
  317.     if pl > 1 then ok := false;
  318.     kl1:=1; kl2:=lenl+1; if (mj[1]='-') or (mj[1]='+') then kl1 := 2;
  319.     for il:=1 to lenl do if mj[il] = '.' then kl2 := il;
  320.     if kl2-kl1 > 38 then ok := false;
  321.     if ok then
  322.       begin
  323.         number:=0; numberdl:=0;
  324.         for il:=kl1 to kl2-1 do number := (ord(mj[il])-48)+10*number;
  325.         if kl2 < lenl+1 then
  326.           for il:=lenl downto kl2+1 do
  327.             numberdl := (ord(mj[il])-48)/10+numberdl/10;
  328.         number := number + numberdl;
  329.         if mj[1]='-' then number := -number;
  330.       end; {if ok}
  331.   end;  (* digval *)
  332.   {}
  333.   procedure TEST;
  334.   var s : string; r : real; ok : boolean;
  335.   begin
  336.     s := '123.41';
  337.     r := DIGVALFN (s, ok);
  338.     if ok then writeln (r) else writeln ('Error in ', s);
  339.     DIGVAL (s, r, ok);
  340.     if ok then writeln (r) else writeln ('Error in ', s);
  341.   end;
  342.  
  343. A2: The conversion can be in the other directorion as well. Here is
  344. how to convert an integer into a string with standard Pascal
  345. routines only.
  346.   function CHRIVLFN (number : integer) : string;
  347.   var il, pl, al : integer;
  348.   cl, mj : string;
  349.   isNeg : boolean;
  350.   begin
  351.     if number < 0 then begin
  352.       isNeg := true; number := -number; end
  353.       else isNeg := false;
  354.     pl := 0; mj := ''; cl := '';
  355.     repeat
  356.       pl := pl + 1;
  357.       al := number mod 10;
  358.       cl := cl + chr(al+48);
  359.       number := number div 10;
  360.     until number = 0;
  361.     if isNeg then begin pl := pl + 1; cl[pl] := '-'; end;
  362.     for il := 1 to pl do mj := mj + cl[pl+1-il];
  363.     chrivlfn := mj;
  364.   end;  (* chrivlfn *)
  365.   {}
  366.   procedure TEST;
  367.   var s : string; j : integer;
  368.   begin
  369.     j := 12341;
  370.     s := CHRIVLFN (j);
  371.     writeln (s);
  372.   end;
  373. --------------------------------------------------------------------
  374.  
  375. From ts@uwasa.fi Sun Apr 28 00:00:56 1996
  376. Subject: Decompiling a TP .EXE
  377.  
  378. 56. *****
  379.  Q: How can I reverse a TP .EXE or .TPU back into source code?
  380.  
  381.  A: This is simply asking too much. You cannot decompile a TP
  382. program in a manner that would give you back the original source.
  383. This method of reverse engineering is not on in actual practice.
  384. Quoting Jeroen Pluimers (jeroenp@dragons.nest.nl) "During the
  385. compilation, important information get's lost about variables,
  386. types, identifiers etc. Writing a Pascal Decompiler is impossible.
  387. The best you can achieve is a disassembler that can help you
  388. recognize some Pascal statements."
  389.    You might note that this question somewhat resembles another
  390. frequent question "How can I convert a TPU unit of one TP version to
  391. another?" which cannot be solved without the original source code.
  392. --------------------------------------------------------------------
  393.  
  394. From ts@uwasa.fi Sun Apr 28 00:00:57 1996
  395. Subject: Calculating date/time differences
  396.  
  397. 57. *****
  398.  Q: How can I calculate the difference between two points of time?
  399.  
  400.  A: This is an unconfirmed answer so be a little careful with it.
  401. But at the very least it shows some interesting information about
  402. Turbo Pascal date/time conventions and how to declare and initialize
  403. typed constants if they are records.
  404.   program TimDifTest;
  405.   uses Dos;
  406.   const a : DateTime
  407.           = (year:1992; month:10; day:24; hour:5; min:29; sec:38);
  408.         b : DateTime
  409.           = (year:1993; month:11; day:23; hour:6; min:30; sec:51);
  410.   var aLong, bLong, cLong : longint;
  411.       c : DateTime;
  412.   begin
  413.     PackTime (a, aLong);
  414.     PackTime (b, bLong);
  415.     cLong := bLong - aLong;
  416.     UnpackTime (cLong, c);
  417.     writeln (c.year-1980, ' ', c.month, ' ', c.day, ' ',
  418.              c.hour, ' ', c.min, ' ', c.sec);
  419.   end.
  420. More generally than for dates between 1980 and 2079, or for more
  421. accurate results, the difference between two date/times can be
  422. calculated using Zeller's congruence (see the item "I want code that
  423. gives the weekday of the given date"). First calculate Zeller's for
  424. both the dates, convert them, and the hour, min, and sec into
  425. seconds, subtract, and convert back.
  426. --------------------------------------------------------------------
  427.  
  428. From ts@uwasa.fi Sun Apr 28 00:00:58 1996
  429. Subject: Stand-alone or from IDE
  430.  
  431. 58. *****
  432.  Q: Is a program running stand-alone or from within the IDE?
  433.  
  434.  A: Not all questions have an answer yet. I posed this question to
  435. the the late UseNet newsgroup comp.lang.pascal, but we have not
  436. found an answer that would be general for all MS-DOS versions. The
  437. closest we have comes from dmurdoch@mast.queensu.ca Duncan Murdoch
  438. (naturally :-). I have done some slight editing of Duncan's
  439. solution.
  440.   uses Dos;
  441.   type Pchar = ^Char;
  442.   function Asciiz2Str (p : Pchar) : string;
  443.   var
  444.     result : string;
  445.     len : byte;
  446.   begin
  447.     len := 0;
  448.     while (p^ <> #0) and (len < 255) do
  449.     begin
  450.       inc(len);
  451.       result[len] := p^;
  452.       inc(longint(p));
  453.     end;
  454.     result[0] := chr(len);
  455.     Asciiz2Str := result;
  456.   end;
  457.   {}
  458.   var parentSeg : ^word;
  459.       p         : pchar;
  460.   begin
  461.     if swap(DosVersion) < $0400 then
  462.       writeln ('Requires Dos 4.0+')
  463.     else begin
  464.       parentSeg := ptr (prefixSeg, $16);
  465.       p := ptr (ParentSeg^-1, 8);
  466.       writeln ('I was launched by ', Asciiz2Str(p));
  467.     end;
  468.   end.
  469. Another suggestion has been that the contents of ParamStr(0) would
  470. show the launching program. I tested this on several configurations
  471. and TP versions and found no evidence that the contention would
  472. hold.
  473. --------------------------------------------------------------------
  474.  
  475. From ts@uwasa.fi Sun Apr 28 00:00:59 1996
  476. Subject: Memory Addressing
  477.  
  478. 59. *****
  479.  Q: Please explain Turbo Pascal memory addressing to me.
  480.  
  481.  A: This is far from an easy question, but let's see what we can do.
  482. The origins of the difficulties are in the design of the 8086 chip
  483. which still restricts all Turbo Pascal applications (which contrary
  484. to Borland Pascal use the original real mode). The 8086 (aka real
  485. mode) addressing is based on 16-bit registers. As you probably know
  486. 2^16 is 65536 which means that you cannot directly point to all
  487. addresses of the lower and upper memory, which ranges from 0 to
  488. 1048575 (2^20-1). Thus all the memory addresses are pointed to into
  489. two parts in TP programs, the segment and the offset. The following
  490. example of the PC's memory illustrates.
  491.  
  492.   Decimal  Hexa-
  493.   address  decimal  Segment  Offset  What
  494.         0   $00000    $0000   $0000  Conventional memory starts
  495.      1043   $00413    $0040   $0013  Base memory in Kb, a word
  496.    655359   $9FFFF    $9000   $FFFF  Conventional memory ends
  497.    655360   $A0000    $A000   $0000  Upper memory begins
  498.   1048575   $FFFFF    $F000   $FFFF  Upper memory ends
  499.  
  500. To exemplify, let's look at some alternative ways we could access
  501. the information about the amount of the base memory. It is very
  502. straight-forward, since in a PC that information is at the fixed
  503. memory location show by the above table. We know this in advance.
  504. Using direct memory accessing we could write
  505.   var memsize : word;
  506.   memsize := MemW [$0040:$0013];
  507.   writeln (memsize);
  508.   {.. or ..}
  509.   var memsize : word absolute $0040:$0013;
  510.   writeln (memsize);
  511. If you are not familiar with the true meaning of pointers, they may
  512. feel confusing, but what they basically are is just what the name
  513. indicates, pointers to memory locations. Study the following
  514. example.
  515.   var memsizePtr : ^word;           { A pointer to a word }
  516.   begin
  517.     memsizePtr := ptr ($40, $13);   { Assign the pointer a value }
  518.     writeln (memsizePtr^);          { Write what is in the address }
  519.   end.                              { that was pointed to }
  520. This was relatively simple, since we knew in advance the location of
  521. the information. Lets look at a case where we do not know that.
  522. Consider
  523.   var x : word;
  524.   begin
  525.     x := 1223;
  526.     writeln (x);
  527.   end.
  528. We have a variable x somewhere in the memory, and we can refer to it
  529. without ever needing to know where the variable actually is in the
  530. memory. But how does one find out if one for some reason wants or
  531. needs to know?
  532.   var x       : word;
  533.       Segment : word;
  534.       Offset  : word;
  535.       y       : ^word;
  536.   begin
  537.     x := 1223;
  538.     writeln (x);
  539.     Segment := Seg(x);
  540.     Offset  := Ofs(x);
  541.     writeln ('Variable x is at $', HEXFN(Segment), ':$', HEXFN(Offset));
  542.     {... one test to ensure that the value really is in there ...}
  543.     writeln (MemW [Segment:Offset]);
  544.     {... another test to demonstrate that the value really is in there ...}
  545.     y := Addr(x);
  546.     writeln (y^);
  547.   end.
  548. Next consider
  549.   var xPtr    : ^word;
  550.       Segment : word;
  551.       Offset  : word;
  552.       yPtr    : ^word;
  553.   begin
  554.     xPtr^ := 1223;
  555.     writeln (xPtr^);
  556.     Segment := Seg(xPtr^);
  557.     Offset  := Ofs(xPtr^);
  558.     writeln ('$', HEXFN(Segment), ':$', HEXFN(Offset));
  559.     {... a test to ensure that the value really is in there ...}
  560.     yPtr := Ptr (Segment, Offset);
  561.     writeln (yPtr^);
  562.   end.
  563. A further aspect of pointers is that you can utilize them to put a
  564. variables onto the heap instead of the data segment so that you
  565. won't run so easily out of space.
  566.   var xPtr : ^word;
  567.   begin
  568.     { Put it onto the heap }
  569.     New (xPtr);
  570.     xPtr^ := 1223;
  571.     writeln (xPtr^);
  572.     { Get rid of it }
  573.     Dispose (xPtr); xPtr := nil;
  574.     readln;
  575.   end.
  576. Let us return to the addressing. The formulas for converting between
  577. the addresses (sent in by Duncan Murdoch) are
  578.   Physical := longint(segment)*16 + offset;
  579.   {}
  580.   Segment  := Physical div 16;
  581.   Offset   := Physical mod 16; { This gives the normalized form }
  582. There are multiple Segment:Offset pairs that refer to the same
  583. address, e.g. $0000:$0413 and $0040:$0013. The normalized addresses,
  584. with the offset in the range of 0 to $F, are, however, unique. An
  585. example $0041:$0003.
  586. --------------------------------------------------------------------
  587.  
  588. From ts@uwasa.fi Sun Apr 28 00:00:60 1996
  589. Subject: Getting a bit from a byte
  590.  
  591. 60. *****
  592.  Q: How do I obtain a bit or bits from a byte, a word or a longint?
  593.  
  594.  A: For bit operations think of the variable as a binary number
  595. instead of a decimal. Consider for example
  596.   var x : word;
  597.   x := 219;
  598. In binary presentation it is
  599.   The word                  0000 0000 1101 1011
  600.   Position in the word      FEDC BA98 7654 3210
  601.  
  602. Say you need the value of bit 6 (the seventh bit) in the word. You
  603. can "and" the following words
  604.   0000 0000 1101 1011    (219)
  605.   0000 0000 0100 0000    ( 64)
  606. In decimal TP notation this amounts to
  607.   var b : word;
  608.   b := x and 64;
  609. The value of b is now
  610.   0000 0000 0100 0000    ( 64)
  611. To get the bit value (0 or 1) you need to shift the result right by
  612. six steps, that this the expression becomes the often seen but
  613. cryptic
  614.   b := (x and 64) shr 6;
  615. which means that the value of b is finally 1 in this example.
  616.  
  617. Ok, but what then if you need the combined value of bits six and
  618. seven. The answer is evident if you consider the binary presentation
  619.   0000 0000 1101 1011    (219)
  620.   0000 0000 1100 0000    (192)
  621. hence
  622.   b := (x and 192) shr 6;
  623. which will give 3 as it should.
  624.  
  625. So far, so good. What if you need to turn on bit nine in a word
  626. without interfering with the other bits. The binary presentation,
  627. again, is the key. You'll have to "or" the following
  628.   0000 0000 1101 1011    (219)
  629.   0000 0010 0000 0000    (512)
  630. that is
  631.   x := x or 512;
  632. This results to
  633.   0000 0010 1101 1011    (731)
  634.  
  635. What if you wish to turn off, say bit 6, in
  636.   0000 0000 1101 1011    (219)
  637.   1111 1111 1011 1111  (65471)
  638. This is achieved by
  639.   x := 219;
  640.   x := x and 65471;
  641. This results to
  642.   0000 0000 1001 1011    (155)
  643.  
  644. Consider the following application as an example. The number of a
  645. PC's floppy disk drives (minus one) is stored in bits 6 and 7 in a
  646. word returned by interrupt $11. This is the code to find out how
  647. many disk drives a PC has.
  648.   uses Dos;
  649.   function NrOfFDiskDrives : byte;
  650.   var regs : registers;
  651.   begin
  652.     Intr ($11, regs);
  653.     NrOfFDiskDrives := ((regs.ax and 192) shr 6) + 1;
  654.   end;
  655.  
  656. A tip from Duncan Murdoch.  You might wish to predefine the
  657. following constants for easier handling
  658.   const bit0  = 1;
  659.         bit1  = 2;
  660.         bit2  = 4;
  661.         :
  662.         bit15 = 32768;
  663.         :
  664.         bit31 = 2147483648;
  665. Or to put it slightly differently as Dr John Stockton
  666. jrs@dclf.npl.co.uk suggests
  667.   const
  668.   bit00=$00000001; bit01=$00000002; bit02=$00000004; bit03=$00000008;
  669.   bit04=$00000010; bit05=$00000020; bit06=$00000040; bit07=$00000080;
  670.   :
  671.   bit28=$10000000; bit29=$20000000; bit30=$40000000; bit31=$80000000;
  672. Finally, you also might want to look at the item "Getting a nybble
  673. from a byte".
  674. --------------------------------------------------------------------
  675. From ts@uwasa.fi Sun Apr 28 00:01:01 1996
  676. Subject: Binary Coded Decimals
  677.  
  678. 61. *****
  679.  Q: What are Binary Coded Decimals? How to convert them?
  680.  
  681.  A: Let us look at full integers only and skip the even more
  682. difficult question of BCD reals and BCD operations.
  683.      Decimal Hexa  BCD
  684.         1      $1    1
  685.         :      $9    9
  686.        10      $A   ..
  687.         :       :    :
  688.        12      $C   ..
  689.         :       :    :
  690.        16     $10   10
  691.        17     $11   11
  692.        18     $12   12
  693.         :       :    :
  694. Consider the last value, that is BCD presentation of 12. The
  695. corresponding hexadecimal is $12 (not $C as in normal decimal to
  696. hexadecimal conversion). The crucial question is how to convert
  697. 12BCD to $12 (or its normal decimal equivalent 18). Here is my
  698. sample code:
  699.   type BCDType = array [0..7] of char;
  700.   {}
  701.   procedure StrToBCD (s : string; var b : BCDType);
  702.   var i, p : byte;
  703.   begin
  704.     FillChar(b, SizeOf(b), '0');
  705.     p := Length (s);
  706.     if p > 8 then exit;
  707.     for i := p downto 1 do b[p-i] := s[i];
  708.   end;  (* strtobcd *)
  709.   {}
  710.   function BCDtoDec (b : BCDType; var ok : boolean) : longint;
  711.   const Digit : array [0..9] of char = '0123456789';
  712.   var i, k : byte;
  713.       y, d : longint;
  714.   begin
  715.     y := 0;
  716.     d := 1;
  717.     ok := false;
  718.     for i := 0 to 7 do begin
  719.       k := Pos (b[i], Digit);
  720.       if k = 0 then exit;
  721.       y := y + (k-1) * d;
  722.       if i < 7 then d := 16 * d;
  723.     end; { for }
  724.     ok := true;
  725.     BCDtoDec := y;
  726.   end;  (* bcdtodec *)
  727.   {}
  728.   {}
  729.   procedure TEST;
  730.   var i  : byte;
  731.       b  : BCDType;
  732.       x  : longint;
  733.       ok : boolean;
  734.       s  : string;
  735.   begin
  736.     s := '12';
  737.     StrToBCD (s, b);
  738.     write ('The BCD value : ');
  739.     for i := 7 downto 0 do write (b[i], ' ');
  740.     writeln;
  741.     x := BCDtoDec (b, ok);
  742.     if ok then writeln ('is ', x, ' as an ordinary decimal')
  743.       else writeln ('Error in BCD');
  744.   end;  (* test *)
  745.   {}
  746.   begin TEST; end.
  747.  
  748. Next we can ask, what if the BCD value is given as an integer.
  749. Simple, first convert the integer into a string. For example in
  750. the procedure TEST put
  751.   Str (12, s);
  752.  
  753. Finally, what about converting an ordinary decimal to the
  754. corresponding BCD but given also as a decimal variable.  For example
  755. 18 --> 12?
  756.   function LHEXFN (decimal : longint) : string;
  757.   const hexDigit : array [0..15] of char = '0123456789ABCDEF';
  758.   var i : byte;
  759.       s : string;
  760.   begin
  761.     FillChar (s, SizeOf(s), ' ');
  762.     s[0] := chr(8);
  763.     for i := 0 to 7 do
  764.       s[8-i] := HexDigit[(decimal shr (4*i)) and $0F];
  765.     lhexfn := s;
  766.   end;  (* lhexfn *)
  767.   {}
  768.   function DecToBCD (x : longint; var ok : boolean) : longint;
  769.   const Digit : array [0..9] of char = '0123456789';
  770.   var hexStr : string;
  771.   var i, k : byte;
  772.       y, d : longint;
  773.   begin
  774.     hexStr := LHEXFN(x);
  775.     y := 0;
  776.     d := 1;
  777.     ok := false;
  778.     for i := 7 downto 0 do begin
  779.       k := Pos (hexStr[i+1], Digit);
  780.       if k = 0 then exit;
  781.       y := y + (k-1) * d;
  782.       if i > 0 then d := 10 * d;
  783.     end; { for }
  784.     ok := true;
  785.     DecToBCD := y;
  786.   end;  (* dectobcd *)
  787.   {}
  788.   procedure TEST2;
  789.   var i    : byte;
  790.       x10  : longint;
  791.       xBCD : longint;
  792.       ok   : boolean;
  793.   begin
  794.     x10 := 18;
  795.     writeln ('The ordinary decimal value : ', x10);
  796.     xBCD := DecToBCD (x10, ok);
  797.     if ok then writeln ('is ', xBCD, ' as a binary coded decimal')
  798.       else writeln ('Error in BCD');
  799.   end;  (* test2 *)
  800.   {}
  801.   begin TEST; end.
  802. --------------------------------------------------------------------
  803.  
  804. From ts@uwasa.fi Sun Apr 28 00:01:02 1996
  805. Subject: Copying with TP
  806.  
  807. 62. *****
  808.  Q: How can I copy a file in a Turbo Pascal program?
  809.  
  810.  A: Here is the code. Take a close look. It has some instructive
  811. features besides the copying, like handling the filemode and using
  812. dynamic variables (using pointers). Note that since the buffer for
  813. the copying is places on the heap you must reserve enough heap. For
  814. example you might have {$M 16384,0,102400}.
  815.   procedure SAFECOPY (fromFile, toFile : string);
  816.   type bufferType = array [1..65535] of char;
  817.   type bufferTypePtr = ^bufferType;  { Use the heap }
  818.   var bufferPtr : bufferTypePtr;     { for the buffer }
  819.       f1, f2 : file;
  820.       bufferSize, readCount, writeCount : word;
  821.       fmSave : byte;              { To store the filemode }
  822.   begin
  823.     bufferSize := SizeOf(bufferType);
  824.     if MaxAvail < bufferSize then exit;  { Assure there is enough memory }
  825.     New (bufferPtr);              { Create the buffer, on the heap }
  826.     fmSave := FileMode;           { Store the filemode }
  827.     FileMode := 0;                { To read also read-only files }
  828.     Assign (f1, fromFile);
  829.     {$I-} Reset (f1, 1); {$I+}    { Note the record size 1, important! }
  830.     if IOResult <> 0 then exit;   { Does the file exist? }
  831.     Assign (f2, toFile);
  832.     {$I-} Reset (f2, 1); {$I+}    { Don't copy on an existing file }
  833.     if IOResult = 0 then begin close (f2); exit; end;
  834.     {$I-} Rewrite (f2, 1); {$I+}  { Open the target }
  835.     if IOResult <> 0 then exit;
  836.     repeat                        { Do the copying }
  837.       BlockRead (f1, bufferPtr^, bufferSize, readCount);
  838.       {$I-} BlockWrite (f2, bufferPtr^, readCount, writeCount); {$I+}
  839.       if IOResult <> 0 then begin close (f1); exit; end;
  840.     until (readCount = 0) or (writeCount <> readCount);
  841.     writeln ('Copied ', fromFile, ' to ', toFile,
  842.              ' ', FileSize(f2), ' bytes');
  843.     close (f1); close (f2);
  844.     FileMode := fmSave;           { Restore the original filemode }
  845.     Dispose (bufferPtr);          { Release the buffer from the heap }
  846.   end;  (* safecopy *)
  847.  
  848. Of course a trivial solution would be to invoke the MS-DOS copy
  849. command using the Exec routine. (See the item "How do I execute an
  850. MS-DOS command from within a TP program?")
  851. --------------------------------------------------------------------
  852.  
  853. From ts@uwasa.fi Sun Apr 28 00:01:03 1996
  854. Subject: C modules in TP
  855.  
  856. 63. *****
  857.  Q: How can I use C code in my Turbo Pascal program?
  858.  
  859.  A: I have very little information on this question, since I do not
  860. program in C myself.  However in reading Turbo Pascal textbooks I
  861. have come across a couple of references I can give.  They are Edward
  862. Mitchell (1993), Borland Pascal Developer's Guide, pp. 60-64, and
  863. Stoker & Ohlsen (1989), Turbo Pascal Advanced Techniques, Ch 4.
  864. --------------------------------------------------------------------
  865.  
  866. From ts@uwasa.fi Sun Apr 28 00:01:04 1996
  867. Subject: Using Turbo Profiler
  868.  
  869. 64. *****
  870.  Q: How do I get started with the Turbo Profiler?
  871.  
  872.  A: Borland's separate Turbo Profiler is a powerful tool for
  873. improving program code and enhancing program performance, but far
  874. from an easy to use. It is an advanced tool. In fact setting it up
  875. the first time is almost a kind of detective work.
  876.    Let's walk through the steps with Turbo Profiler version 1.01 to
  877. see where a running Turbo Pascal program takes its time.
  878. Assume a working directory r:\
  879. 1. Copy the target .PAS file to r:\
  880. 2. Compile it with TURBO.EXE using the following Compiler and
  881.    Debugger options. The standalone debugging option is crucial.
  882.      Code generation
  883.       [ ] Force far calls        [X] Word align data
  884.       [ ] Overlays allowed       [ ] 286 instructions
  885.      Runtime errors             Syntax options
  886.       [ ] Range checking         [X] Strict var-strings
  887.       [X] Stack checking         [ ] Complete boolean eval
  888.       [ ] I/O checking           [X] Extended syntax
  889.       [ ] Overflow checking      [ ] Typed @ operator
  890.                                  [ ] Open parameters
  891.      Debugging
  892.       [X] Debug information     Numeric processing
  893.       [X] Local symbols          [ ] 8087/80287
  894.                                  [ ] Emulation
  895.      Debugging         Display swapping
  896.       [X] Integrated    ( ) None
  897.       [X] Standalone    () Smart
  898.                         ( ) Always
  899. 3) Call TPROF.EXE
  900. 4) Load the .EXE file produced by compilation in item 2.
  901. 5) Choose from the TPROF menus
  902.      Statistics
  903.        Profiling options...
  904.          Profile mode
  905.           () Active    ( ) Passive
  906.           Run count
  907.            1
  908.           Maximum areas
  909.            200
  910. 6) Choose from the TPROF menus
  911.       Options
  912.        Save options...
  913.       [X] Options
  914.       [ ] Layout
  915.       [ ] Macros
  916.      Save To
  917.       r:\tfconfig.tf
  918. 7) Press Alt-F10 for the Local Menu. Choose
  919.      Add areas
  920.        All routines
  921. and so on.
  922. 8) Choose Run from the TPROF menus (or F9)
  923. 9) Choose from the TPROF menus
  924.       Print
  925.        Options...
  926.      Width
  927.       80
  928.      Height
  929.       9999
  930.       ( ) Printer     ( ) Graphics
  931.       () File        () ASCII
  932.      Destination File
  933.       r:\report.lst
  934. 10) Print
  935.        Module...
  936.          All modules
  937.          Statistics
  938.            Overwrite
  939. Also see Edward Mitchell (1993), Borland Pascal Developer's Guide.
  940. It has a a very instructive chapter "Program Optimization" on the
  941. Turbo Profiler. The material in the Turbo Profiler manual is so
  942. complicated that additional guidance like Mitchell's is very much
  943. needed.
  944. --------------------------------------------------------------------
  945.  
  946. From ts@uwasa.fi Sun Apr 28 00:01:05 1996
  947. Subject: Detecting shift status
  948.  
  949. 65. *****
  950.  Q: How can I detect if the shift/ctrl/alt etc key is pressed? I
  951. know how to get the scan codes with the ReadKey function, but I
  952. can't find the procedure for detecting these keys.
  953.  
  954.  A: Detecting pressing the special keys or getting the toggle status
  955. cannot be done with ReadKey. You'll need to access the Keyboard
  956. Flags Byte at $0040:$0017. You can do this either by a direct "Mem"
  957. access, or using interrupt $16 function $02. For more details
  958. including the bitfields for the shift flags see in Ralf Brown's
  959. interrupt list ftp://garbo.uwasa.fi/pc/programming/inter49a.zip (or
  960. whatever is the current version). For example to see if the alt key
  961. is pressed you can use
  962.   uses Dos;
  963.   function ALTDOWN : boolean;
  964.   var regs : registers;
  965.   begin
  966.     FillChar (regs, SizeOf(regs), 0);
  967.     regs.ah := $02;
  968.     Intr ($16, regs);
  969.     altdown := (regs.al and $08) = $08;
  970.   end;
  971. For the enhanced keyboard flags see interrupt $16 function $12. It
  972. can distinguish also between the right and the left alt and ctlr
  973. keys.
  974.    A tip from Martijn Leisink martijnl@sci.kun.nl. Be careful [if
  975. you use the $0040:$0017 memory position to set a toggle]: On several
  976. computers you have to call int 16h after the new setting is shown by
  977. the LED's on the keyboard. Not doing so might give the user wrong
  978. information.
  979.    A tip from Dr John Stockton jrs@dclf.npl.co.uk. Going via a
  980. BytePointer set to Ptr(Seg0040, $0017) is almost as easy as "Mem",
  981. and also works in Protected mode.
  982. --------------------------------------------------------------------
  983.  
  984. From ts@uwasa.fi Sun Apr 28 00:01:06 1996
  985. Subject: Base 10 logarithm
  986.  
  987. 66. *****
  988.  Q: How do I get a base 10 logarithm in TP?
  989.  
  990.  A: Just define
  991.      function log (x : real) : real;
  992.      begin log := ln(x) / ln(10); end;
  993. This result is based on some elementary math. By definition
  994. y = log(x) in base 10 is equivalent to x = 10^y (where the ^
  995. indicates an exponent). Thus ln(x) = y ln(10) and hence
  996. y = ln(x) / ln(10).
  997. --------------------------------------------------------------------
  998.  
  999. From ts@uwasa.fi Sun Apr 28 00:01:07 1996
  1000. Subject: Replacing Delay procedure
  1001.  
  1002. 67. *****
  1003.  Q: If Delay procedure does not work properly, how do I fix it?
  1004.  
  1005.  A: The Delay procedure in the Crt unit delays a specified number of
  1006. milliseconds. It is declared as "procedure Delay(MS: Word);". There
  1007. are two problems. The procedure requires using the Crt unit and
  1008. there is a bug in it in TP 6.0, at least. The alternative is to use
  1009. the procedure GetTime(var Hour, Minute, Second, Sec100: Word) as
  1010. shown by the skeleton below
  1011.   GetTime (...)
  1012.   initialTime := ...
  1013.   repeat
  1014.     GetTime (...)
  1015.     interval := ... - initialTime;
  1016.   until interval >= YourDelay;
  1017. There are two things you will have to see to. You will have to
  1018. convert the time to sec100, and you will have to take care of the
  1019. possibility of the interval spanning the midnight. If you do not
  1020. wish to program the alternative Delay procedure yourself, you can
  1021. use "DOSDELAY Delay without needing the Crt unit" from TSUNTD.TPU
  1022. from ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  1023.  
  1024.  A2: Dr John Stockton jrs@dclf.npl.co.uk suggested procedure that is
  1025. expanded below. It has the advantage of being concise and working in
  1026. the protected mode. The disadvantage is that it requires a later TP
  1027. version. The solution is quite instructive.
  1028.   uses Dos;
  1029.   {... John's procedure ...}
  1030.   procedure WAIT (SecondsDelay : real) ;
  1031.   Var Tptr : ^longint ; Finish : longint ;
  1032.   begin
  1033.     Tptr := Ptr(Seg0040, $006C) ;
  1034.     Finish := Tptr^ + Round(18.2*SecondsDelay) ;
  1035.     repeat until Tptr^ > Finish ;
  1036.   end;
  1037.   {... now let's test it ...}
  1038.   var h1, m1, s1, sa100 : word;
  1039.       h2, m2, s2, sb100 : word;
  1040.   begin
  1041.     GetTime (h1, m1, s1, sa100);
  1042.     WAIT (3);
  1043.     GetTime (h2, m2, s2, sb100);
  1044.     writeln (h1, ':', m1, ':', s1, '.' ,sa100);
  1045.     writeln (h2, ':', m2, ':', s2, '.' ,sb100);
  1046.   end.
  1047. --------------------------------------------------------------------
  1048.  
  1049. From ts@uwasa.fi Sun Apr 28 00:01:08 1996
  1050. Subject: TP program memory requirement
  1051.  
  1052. 68. *****
  1053.  Q: How much memory will my TP program require?
  1054.  
  1055.  A: Get MAPMEM.EXE from ftp://garbo.uwasa.fi/pc/memutil/tsrcom35.zip
  1056. and put the following code within your Turbo Pascal program:
  1057.   Program faq;
  1058.   uses Dos;
  1059.   :
  1060.   SwapVectors;
  1061.   Exec (GetEnv('comspec'), '/c mapmem');
  1062.   Swapvectors;
  1063. Then you'll see a MAPMEM output something like this
  1064.   Psp  Cnt   Size Name       Command Line        Hooked Vectors
  1065.   ---- --- ------ ---------- ------------------- --------------
  1066.         2  26,896 DOS
  1067.   0694  2   3,392 COMMAND                        2E
  1068.         1      64 ---free---
  1069.   0776  2   1,488 MARK       scrollit
  1070.   07D6  2  70,816 FAQ                            FF
  1071.   1923  3   2,752 command                        22 23 24
  1072.   19D2  2 549,712 ---free---
  1073.           655,344 ---total--
  1074. The memory requirement of your program FAQ.PAS is 70,816. Do not
  1075. confuse this figure with the physica size of your program. The
  1076. memory requirement affected among other things by the Memory
  1077. Allocation Sizes Directive. For example you might have
  1078. {$M 16384,0,50000}
  1079.  
  1080. -Date: Sun, 12 Jun 1994 10:22:18
  1081. -From: dmurdoch@mast.queensu.ca (Duncan Murdoch)
  1082. -Newsgroups: comp.lang.pascal
  1083. -Subject: Re: How much memory will my TP program require?
  1084.  
  1085.    I think this is a hard question, and probably needs a longer
  1086. answer than you gave.  Yours isn't quite right, because TP will
  1087. allocate memory that it doesn't need if you set the heapmax
  1088. parameter too high.  Your program will run in less memory than
  1089. MAPMEM reports. Here's a quick attempt at it:
  1090.    TP DOS programs use memory in 4 or 5 blocks:  fixed code, static
  1091. data, the stack, sometimes overlaid code, and the heap.  TP Windows
  1092. programs add a local heap to this list, but don't use overlays.  The
  1093. discussion below deals with real mode DOS programs.
  1094.    The size of the code is determined by which procedures and
  1095. functions you use in your program.  In DOS, if you don't use
  1096. overlays, this is all fixed code, and the size is reported as "Code
  1097. size" in the Compile| Information listing in the IDE.  The ways to
  1098. reduce it are to use fewer procedures or make them smaller, or move
  1099. them to overlays.
  1100.    Static data consists of all the global variables and typed
  1101. constants in every unit.  It is reported as "Data size" in the
  1102. Compile|Information listing.  You can reduce it by declaring fewer
  1103. or smaller variables.
  1104.    If you use the $O directive to move code to overlays, then those
  1105. units won't count as part of your fixed code needs.  You will need
  1106. an overlay buffer at run-time; by default, it's the size of the
  1107. largest unit you use, but normally you'll change the size with
  1108. OvrSetBuf.  It's difficult to work out the best size of this block
  1109. except by trial and error:  if your program spends too much time
  1110. swapping, then make it larger; if you run out of memory, make it
  1111. smaller.  You'll need to use the .MAP file (see the Options| Linker
  1112. dialog to create one) to find the size of each unit.  Remember to
  1113. subtract the size of overlaid units from the reported "Code size"
  1114. when working out the size of fixed code.
  1115.   The stack is used for local variables in procedures.  Its size is
  1116. controlled by the first parameter to the $M directive; the default
  1117. size is 16K.  It's hard to predict exactly how much stack space your
  1118. program will use.  One way is to keep reducing the value until your
  1119. program aborts with a stack overflow, then use a slightly larger
  1120. value.  Another way is to fill the stack with a fixed value at the
  1121. start of your program, and at the end, see how many values were
  1122. changed.  Again, it's a good idea to allow for a margin of safety,
  1123. because hardware interrupts will use this space, and their size is
  1124. hard to predict.
  1125.    The heap is where New and Getmem get their allocated memory.  The
  1126. size is controlled by the 2nd and 3rd parameters to the $M
  1127. directive.  The heapmin value will always be allocated; if extra
  1128. memory is available, your program will ask for as much as possible,
  1129. up to heapmax.  If not enough memory is available to load all your
  1130. fixed code, data, stack and heapmin, DOS will refuse to load your
  1131. program.  You have nearly complete control over the size of the heap
  1132. that you need, determined by how much you use New and Getmem. The
  1133. only exception is that some of the standard units use heap space;
  1134. GRAPH and all the TurboVision units are examples.  To find how much
  1135. your program actually uses, you can reduce Heapmax until it fails,
  1136. fill the heap with a special value and look for changes, or monitor
  1137. the value of HeapPtr as your program progresses.
  1138. --------------------------------------------------------------------
  1139.  
  1140. From ts@uwasa.fi Sun Apr 28 00:01:09 1996
  1141. Subject: Detecting a CD-ROM drive?
  1142.  
  1143. 69. *****
  1144.  Q: How to detect if a drive is a CD-ROM drive?
  1145.  
  1146.  A: There are several methods to do this. Here is one option.
  1147.   (* Is a drive a CD-ROM with MSCDEX driver installed *)
  1148.   function CDROMFN (drive : char) : boolean;
  1149.   var regs : registers;
  1150.   begin
  1151.     cdromfn := false;
  1152.     if swap(DosVersion) < $0200 then exit;
  1153.     drive := UpCase(drive);
  1154.     if (drive < 'A') or (drive > 'Z') then exit;
  1155.     FillChar (regs, SizeOf(regs), 0);
  1156.     regs.cx := ord(drive) - ord('A');
  1157.     regs.ax := $150B;
  1158.     Intr ($2F, regs);
  1159.     cdromfn := (regs.ax <> 0) and (regs.bx = $ADAD);
  1160.   end;  (* cdromfn *)
  1161. The other relevant $2F interrupt functions you can use are $1500,
  1162. $1501, and in particular $150D.
  1163. --------------------------------------------------------------------
  1164.  
  1165. From ts@uwasa.fi Sun Apr 28 00:01:10 1996
  1166. Subject: Array of chars into string
  1167.  
  1168. 70. *****
  1169.  Q: How do I convert an array of characters to a string? More
  1170. specifically, I haven't been able to convert an array of characters
  1171. into a string, so that I can write it to a file. The only way I have
  1172. been able to do it, is writing 1 char at a time.
  1173.  
  1174.  A: Carefully study these two simple test examples.  Note the
  1175. difference in the array's dimensions in the tests.
  1176.  
  1177.   type atype = array [0..20] of char;
  1178.   type stype = string[20];
  1179.   var s : stype;
  1180.       a : atype absolute s;
  1181.   begin
  1182.     FillChar (a, SizeOf(a), '*');
  1183.     s[0] := chr(20);
  1184.     writeln (s);
  1185.   end.
  1186.  
  1187.   type atype = array [1..20] of char;
  1188.   var s : string;
  1189.       a : atype;
  1190.   begin
  1191.     FillChar (a, Sizeof(a), '*');
  1192.     Move (a, s[1], 20);
  1193.     s[0] := chr(20);
  1194.     writeln (s);
  1195.   end.
  1196.  
  1197. Of course, you could also assign the array's characters one by one
  1198. to the string using a simple for loop (left as an exercise), but the
  1199. above methods are more efficient.
  1200. --------------------------------------------------------------------
  1201.  
  1202. From ts@uwasa.fi Sun Apr 28 00:01:11 1996
  1203. Subject: Graphics programming primer
  1204.  
  1205. 71. *****
  1206.  Q: How do I get started with graphics programming?
  1207.  
  1208.  A:
  1209.   (* This simple test shows the rudiments of getting started with Turbo
  1210.      Pascal graphics programming *)
  1211.   uses Crt, Graph;
  1212.   var grDriver : integer;
  1213.       grMode   : integer;
  1214.       ErrCode  : integer;
  1215.       i, j     : integer;
  1216.       xm, ym   : integer;
  1217.   const CharSize : integer = 3;
  1218.   begin
  1219.     { Request graphics driver autodetection }
  1220.     grDriver := Detect;
  1221.     { Initialize graphics system and put hardware into graphics mode }
  1222.     { The relevant .bgi driver is needed in the current directory
  1223.       for example egavga.bgi }
  1224.     InitGraph (grDriver, grMode, ' ');
  1225.     { Return an error code for the previous graphic operation }
  1226.     ErrCode := GraphResult;
  1227.     { Test for initialialization success }
  1228.     if ErrCode <> grOk then begin
  1229.       Writeln ('Graphics error:', GraphErrorMsg(ErrCode)); halt; end;
  1230.     { Clear the output device and home the current pointer }
  1231.     ClearDevice;
  1232.     {}
  1233.     { Use your own coordinates }
  1234.     xm := Round (GetMaxX / 100.0);
  1235.     ym := Round (GetMaxY / 100.0);
  1236.     {}
  1237.     { Set the current line width and style, optional }
  1238.     SetLineStyle (SolidLn, 0, ThickWidth);
  1239.     { Set the drawing color }
  1240.     SetColor (Yellow);
  1241.     { Draw a line }
  1242.     Line (70*xm, 50*ym, 90*xm, 80*ym);
  1243.     {}
  1244.     { Drawing bars }
  1245.     { Set the fill pattern and color }
  1246.     SetFillStyle (SolidFill, Red);
  1247.     Bar (0, 0, 25*xm, 25*ym);
  1248.     {}
  1249.     SetColor (Magenta);
  1250.     SetFillStyle (SolidFill, Blue);
  1251.     Bar3D (30*xm, 20*ym, 50*xm, 60*ym, 8*xm, TopOn);
  1252.     {}
  1253.     { Writing text in the graphics mode }
  1254.     { Set the drawing color }
  1255.     SetColor (LightCyan);
  1256.     { Set the current background color }
  1257.     SetBkColor (Black);
  1258.     { Set style for text output in graphics mode }
  1259.     SetTextStyle(DefaultFont, HorizDir, CharSize);
  1260.     OutTextXY (0, 80*ym, 'Press any key');
  1261.     {}
  1262.     repeat until KeyPressed;
  1263.     {}
  1264.     { Restore the original screen mode before graphics was initialized }
  1265.     RestoreCrtMode;
  1266.     writeln ('That''s all folks');
  1267.     { Shut down the graphics system }
  1268.     CloseGraph;
  1269.   end.
  1270. For an example what you can do with graphics, see
  1271.  111673 Oct 8 1993 ftp://garbo.uwasa.fi/pc/ts/tsdemo16.zip
  1272.  tsdemo16.zip Assorted graphics demonstrations of functions etc
  1273. (or whatever is the current version).
  1274. --------------------------------------------------------------------
  1275.  
  1276. From ts@uwasa.fi Sun Apr 28 00:01:12 1996
  1277. Subject: Sorting it out
  1278.  
  1279. 72. *****
  1280.  Q: Where to I find the different sorting source codes?
  1281.  
  1282.  A: I'll answer very briefly by giving two references:
  1283.  303771 May 2 1991 ftp://garbo.uwasa.fi/pc/turbopas/nrpas13.zip
  1284.  nrpas13.zip Numerical Recipes Pascal shareware version
  1285. and
  1286.  Gary Martin (1992), Turbo Pascal, Theory and Practice of Good
  1287. Programming, Chapter 15.
  1288. --------------------------------------------------------------------
  1289.  
  1290. From ts@uwasa.fi Sun Apr 28 00:01:13 1996
  1291. Subject: TP units
  1292.  
  1293. 73. *****
  1294.  Q: A beginner's how to write and compile units.
  1295.  
  1296.  A1: Many of the text-books in the bibliography section of this FAQ
  1297. discuss using units in Turbo Pascal. For example see Tom Swan
  1298. (1989), Mastering Turbo Pascal 5.5, Chapters 9 and 10 for a more
  1299. detailed discussion than the rudiments given in the current item.
  1300. Likewise see your Turbo Pascal (7.0) User's Guide Chapter 6, "Turbo
  1301. Pascal units".
  1302.    You can and need to write your own units if you need recurring or
  1303. common routines in your programs and/or your program becomes so big
  1304. that it cannot be handled as a single entity.
  1305.    A Turbo Pascal unit is a separate file which you compile. The
  1306. following trivial example to calculate the sum of two reals
  1307. illustrates the basic structure of a unit.
  1308.   { The name of this file must be faq73.pas to correspond. }
  1309.   unit faq73;
  1310.   {}
  1311.   { The interface section lists definitions and routines that are }
  1312.   { available to the other programs or units. }
  1313.   interface
  1314.   function SUMFN (a, b : real) : real;
  1315.   {}
  1316.   { The implementation section contains the actual unit program }
  1317.   implementation
  1318.   function SUMFN (a, b : real) : real;
  1319.   begin
  1320.     sumfn := a + b;
  1321.   end;
  1322.   {}
  1323.   end.
  1324. When you compile the file FAQ73.PAS a unit FAQ73.TPU results. Next
  1325. an example utilizing the faq73 unit in the main program.
  1326.   uses faq73;
  1327.   {}
  1328.   procedure TEST;
  1329.   var x, y, z : real;
  1330.   begin
  1331.      x := 12.34;
  1332.      y := 56.78;
  1333.      z := SUMFN (x, y);
  1334.      writeln (z);
  1335.   end;
  1336.   {}
  1337.   begin
  1338.     TEST;
  1339.   end.
  1340.  
  1341.  A2: Most often you would be compiling a Turbo Pascal program
  1342. using the IDE (Integrated Development Environment). If you have
  1343. precompiled units you must see to it that you have informed the IDE
  1344. of the path to them.
  1345.    Press F10 and invoke the "Options" menu (or press alt-O). Select
  1346. "Directories...". Press tab two times to get to "Unit directories"
  1347. and edit the path accordingly. Here is what I have entered myself
  1348.   EXE & TPU directory  r:\
  1349.   Include directories  r:\
  1350.   Unit directories     f:\progs\turbo70\tpu70
  1351.   Object directories   f:\progs\turbo70\tpu70
  1352. As you see I keep all my precompiled Turbo Pascal 7.0 units in the
  1353. f:\progs\turbo70\tpu70 directory.
  1354. --------------------------------------------------------------------
  1355.  
  1356. From ts@uwasa.fi Sun Apr 28 00:01:14 1996
  1357. Subject: Beginners' pointers
  1358.  
  1359. 74. *****
  1360.  Q: What are and how do I use pointers?
  1361.  
  1362.  A: This is a beginner's simplified introduction. A pointer is a
  1363. variable type used to hold the address of another variable, that is
  1364. to point to it. Pointers are used to
  1365.  1) To refer to and manipulate variables indirectly.
  1366.  2) In Turbo Pascal to obtain access to the heap storage area, which
  1367.     is not restricted to 64Kbytes.
  1368. Consider the following example
  1369.   {$M 16384,0,80000}
  1370.   var yPtr : ^real;
  1371.   begin
  1372.     New(yPtr);
  1373.     yPtr^ := 3.14159;
  1374.     writeln ('2 times pi = ',  2.0 * yPtr^);
  1375.     Dispose(yPtr);
  1376.     yPtr := nil;
  1377.   end.
  1378. Before we can discuss pointers we have to consider some rudiments of
  1379. what a kind of a memory model a compiled Turbo Pascal program uses.
  1380. This is a highly simplified presentation. For a more detailed
  1381. presentation of the TP memory model see for example Tischer (1990b).
  1382.   +-------------------------+
  1383.   | Heap                    |
  1384.   |-------------------------|
  1385.   | Data Segment            |
  1386.   |-------------------------|
  1387.   | Code                    |
  1388.   |-------------------------|
  1389.   | Program Segment Prefix  |
  1390.   +-------------------------+
  1391. When you write and compile a Turbo Pascal program it usually
  1392. consists of (this is a simplification!) of the three lowest parts.
  1393. When you define a global variable, it goes to the Data Segment. For
  1394. example defining at the beginning of your program
  1395.  var x : real;
  1396. requires 6 bytes from the data segment. (Local variables are placed
  1397. on the stack.)
  1398.    Now, the catch is that because of the underlying 16-bit nature of
  1399. MS-DOS, the size of the data segment cannot exceed 64Kb. On occasion
  1400. the 64Kb is insufficient. However, if you use pointers, the
  1401. corresponding variable values are held on the heap instead of the
  1402. data segment or the stack. Before you can use the heap, you have to
  1403. reserve it for your program. The following compiler directive makes
  1404. a heap of 80000 bytes available to your program {$M 16384,0,80000}.
  1405. (The syntax is {$M Stack size, Low heap limit, High heap limit}).
  1406.    With pointers you do not refer to a variable directly, but you
  1407. point to it. For example, define
  1408.   var yPtr : ^real;
  1409. Before you can use this pointer, you have to create this new dynamic
  1410. variable as follows:
  1411.   New(yPtr);
  1412. The New(yPtr) statement "Creates a new dynamic variable and sets a
  1413. pointer variable to point to it." This pointer, yPtr, will point to
  1414. the actual value, which the program puts on the heap. In your
  1415. program you can write, for example
  1416.   yPtr^ := 3.14159;
  1417. Think about the difference between yPtr and yPtr^. The former
  1418. contains the value of the memory address where you now have put the
  1419. value 3.14159. The latter gives that value. Hence yPtr^ can be used
  1420. like any ordinary real variable. The difference is that it is on the
  1421. heap, not on the data segment (or stack). Thus you can now use this
  1422. pointer. For example you n write
  1423.  writeln ('2 times pi = ',  2.0 * yPtr^);
  1424. When you do not need the pointer any more in your program you can
  1425. dispose of it to release the memory allocated for other purposes:
  1426.   Dispose(yPtr);
  1427.   yPtr := nil;
  1428. "After a call to Dispose, the value of yPtr is undefined and it is
  1429. an error to reference yPtr. The reserved word nil denotes a pointer
  1430. type constant that does not point to anything." Setting yPtr := nil
  1431. is just good programming practice, because then you can later easily
  1432. test whether the pointer is available or not. Disposing of a pointer
  1433. within your program is not necessary unless the amount of memory is
  1434. a critical consideration in your program. The heap will be released
  1435. when your program terminates.
  1436.   To recount. What yPtr actually contains is the memory address of
  1437. the value on the heap. When you write yPtr^, the caret indicates
  1438. that you do not mean the pointer itself, but the pointed memory
  1439. location in the heap. In this example that memory location in the
  1440. heap was made to contain 3.14159.
  1441.    You can also define the pointer types. Our second example
  1442. illustrates. It displays the squares from one to ten.
  1443.   {$M 16384,0,80000}
  1444.   type arrayType = array [1..10] of real;
  1445.   type arrayPtrType = ^arrayType;
  1446.   var A : arrayPtrType;
  1447.       i : integer;
  1448.   begin
  1449.     if SizeOf(arrayType) > MaxAvail then begin
  1450.       writeln ('Out of memory');
  1451.       halt;
  1452.     end;
  1453.     New(A);
  1454.     for i := 1 to 10 do A^[i] := i*i;
  1455.     writeln (A^[9]);
  1456.   end.
  1457. For an actual application using pointers, see the item "How can I
  1458. copy a file in a Turbo Pascal program?"
  1459. --------------------------------------------------------------------
  1460.  
  1461. From ts@uwasa.fi Sun Apr 28 00:01:15 1996
  1462. Subject: Reading errorlevel
  1463.  
  1464. 75. *****
  1465.  Q: How can I read another program's errorlevel value in TP?
  1466.  
  1467.  A: This question is best answered by an example. Here is a very
  1468. elementary program that returns errorlevel 14 on exiting.
  1469.   program faq2;
  1470.   begin
  1471.     writeln ('Hello world...');
  1472.     halt(14);
  1473.   end.
  1474. Below is the program that calls FAQ2.EXE and detects its errorlevel.
  1475.   {$M 2000,0,0}
  1476.   uses Dos;
  1477.   begin
  1478.     SwapVectors;
  1479.     Exec ('r:\faq2.exe', '');  (* Execution *)
  1480.     SwapVectors;
  1481.     WriteLn('...back from Exec');
  1482.     if DosError <> 0 then
  1483.       WriteLn('Dos error #', DosError)
  1484.       else
  1485.       WriteLn('Success; child process errorlevel = ', lo(DosExitCode));
  1486.   end.
  1487. The output should be
  1488.   Hello world...
  1489.   ...back from Exec
  1490.   Success; child process errorlevel = 14
  1491. --------------------------------------------------------------------
  1492.  
  1493.